package id.co.shuba.spamobile.util;

import id.co.shuba.spamobile.App;
import id.co.shuba.spamobile.model.AuditInfo;
import id.co.shuba.spamobile.model.Column;
import id.co.shuba.spamobile.model.FormField;
import id.co.shuba.spamobile.model.PrimaryKey;
import id.co.shuba.spamobile.model.Table;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;

public class AuditUtil {			
	public static ArrayList<AuditInfo> getAuditList(Class<?> clazz,
			Object oldObj, Object newObj, String user, String spinnerDefault, String... exceptions) throws Exception {
				
		if (newObj == null)
			throw new Exception("newObj is null");
		if (oldObj != null && oldObj.getClass() != clazz)
			throw new Exception("Mismatch type oldObj");
		if (newObj.getClass() != clazz)
			throw new Exception("Mismatch type newObj");
						
		Arrays.sort(exceptions);
		ArrayList<AuditInfo> list = new ArrayList<AuditInfo>();
		StringBuilder primaryKey = new StringBuilder();
		String date = App.getProcessDateTime();
		String actType = oldObj == null? "ADD" : "UPDATE";
		
		Table tableAnnotation = clazz.getAnnotation(Table.class);		
		String table = tableAnnotation == null? clazz.getName() : tableAnnotation.value();		
		
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {			
			String columnName = field.getName();
			
			if (Arrays.binarySearch(exceptions, columnName) >= 0)
				continue;
			
			Column col = field.getAnnotation(Column.class);
			if (col != null) {				
				columnName = col.value();
				if (Arrays.binarySearch(exceptions, columnName) >= 0)
					continue;
			}
			
			field.setAccessible(true);
						
			Object newValue = field.get(newObj);		
			
			if (field.getAnnotation(PrimaryKey.class) != null) {
				primaryKey.append(String.format(",%s = %s", columnName, newValue));
				continue;
			}
			
			Object oldValue = null;
			if (oldObj != null)
					oldValue = field.get(oldObj);			
				
			String strOldValue = toString(oldValue);
			String strNewValue = toString(newValue);
			
			strOldValue = strOldValue == null? "" : strOldValue;
			strNewValue = strNewValue == null? "" : strNewValue;
			
			AuditInfo audit = new AuditInfo();
			audit.setTableName(table);
			audit.setActType(actType);
			audit.setOldValue(strOldValue);
			audit.setNewValue(strNewValue);
			audit.setUpdateBy(user);
			audit.setUpdateDate(date);
			audit.setFieldName(columnName);			
			
			String defaultValue = getDefaultValue(field, col);
			
			if ((oldObj == null && strNewValue != null && !strNewValue.equals(defaultValue) && !strNewValue.equals(spinnerDefault))
					|| (strOldValue != null && !strOldValue.equals(strNewValue))) {
				list.add(audit);				
			} 			
		}
		
		primaryKey.deleteCharAt(0);
		
		for (AuditInfo info : list) {
			info.setPrimaryKey(primaryKey.toString());
		}
		
		return list;
	}	
	
	public static boolean isFormChanged(Class<?> clazz,
			Object oldObj, Object newObj, String spinnerDefault, String... exceptions) throws Exception {
				
		if (newObj == null)
			throw new Exception("newObj is null");
		if (oldObj != null && oldObj.getClass() != clazz)
			throw new Exception("Mismatch type oldObj");
		if (newObj.getClass() != clazz)
			throw new Exception("Mismatch type newObj");
															
		Arrays.sort(exceptions);
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {	
			if (field.getAnnotation(FormField.class) == null) 				
				continue;
			
			Column col = field.getAnnotation(Column.class);
			
			if (Arrays.binarySearch(exceptions, field.getName()) != -1)
				continue;
			
			if (col != null && Arrays.binarySearch(exceptions, col.value()) != -1)
				continue;
			
			field.setAccessible(true);
																	
			String defaultValue = getDefaultValue(field, col);
						
			Object newValue = field.get(newObj);										
			Object oldValue = null;
			if (oldObj != null)
				oldValue = field.get(oldObj);			
				
			String strOldValue = toString(oldValue);
			String strNewValue = toString(newValue);					
			
			if ((Str.isNullOrEmpty(strOldValue) && !Str.isNullOrEmpty(strNewValue) && !strNewValue.equals(defaultValue) && !strNewValue.equals(spinnerDefault))
					|| (!Str.isNullOrEmpty(strOldValue) && !strOldValue.equals(strNewValue)))
				return true;
		}
		
		return false;
	}
	
	private static String getDefaultValue(Field field, Column col) {
		String defaultVal = "";
		Class<?> clazz = field.getType();
		if (clazz.equals(int.class)
				|| clazz.equals(long.class)
				|| clazz.equals(short.class))
			defaultVal = String.valueOf(0);
		else if (clazz.equals(float.class))
			defaultVal = String.valueOf(0f);
		else if (clazz.equals(double.class))
			defaultVal = String.valueOf(0d);
		else if (clazz.equals(boolean.class))
			defaultVal = String.valueOf(false);
				
		if (col != null && !"".equals(col.defaultValue()))
			defaultVal = col.defaultValue();
		
		return defaultVal;
	}
	
	public static String getPrimaryKeyInfo(Object obj) throws Exception {
		if (obj == null) return null;
		
		StringBuilder primaryKey = new StringBuilder();
		
		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field field : fields) {									
			String columnName = field.getName();
			
			Object col = field.getAnnotation(Column.class);
			if (col != null) {
				Column columnAttr = (Column) col;
				columnName = columnAttr.value();
			}
			
			field.setAccessible(true);
						
			if (field.getAnnotation(PrimaryKey.class) != null) {
				String value = toString(field.get(obj));
				primaryKey.append(String.format(",%s = %s", columnName, value));				
			} 			
		}
		
		if (primaryKey.length() > 0)
			primaryKey.deleteCharAt(0);
		
		return primaryKey.toString();
	}
	
	public static String toString(Object obj) {
		if (obj == null) return "";
		
		return String.valueOf(obj);
	}		
}
